package cn.wch.ch934xlib;

import android.app.Application;
import android.content.Context;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import cn.wch.ch934xlib.assist.GPIOAssist;
import cn.wch.ch934xlib.assist.ModeAssist;
import cn.wch.ch934xlib.callback.IDataCallback;
import cn.wch.ch934xlib.callback.IUsbStateChange;
import cn.wch.ch934xlib.chip.ChipEntity;
import cn.wch.ch934xlib.chip.ChipInformationUtil;
import cn.wch.ch934xlib.chip.ChipParameterUtil;
import cn.wch.ch934xlib.chip.ChipType;
import cn.wch.ch934xlib.chip.ChipValidUtil;
import cn.wch.ch934xlib.chip.Mode;
import cn.wch.ch934xlib.connet.Connection;
import cn.wch.ch934xlib.constant.Constant;
import cn.wch.ch934xlib.exception.ChipException;
import cn.wch.ch934xlib.exception.NoPermissionException;
import cn.wch.ch934xlib.exception.UartLibException;
import cn.wch.ch934xlib.gpio.GPIO_DIR;
import cn.wch.ch934xlib.gpio.GPIO_VALUE;
import cn.wch.ch934xlib.receiver.UsbReceiver;
import cn.wch.ch934xlib.utils.LogUtil;
import cn.wch.ch934xlib.utils.UsbUtil;


public final class CH934XManager implements IUsbStateChange {
    private static CH934XManager CH934XManager;

    /**
     * 用于获取全局唯一实例
     * @return
     */
    public static CH934XManager getInstance() {
        if(CH934XManager ==null){
            synchronized (CH934XManager.class){
                CH934XManager =new CH934XManager();
            }
        }
        return CH934XManager;
    }

    private UsbManager usbManager;
    private UsbReceiver receiver;
    private HashMap<UsbDevice, Connection> connectionMap;
    private IUsbStateChange iUsbStateChange;
    private AtomicInteger atomicInteger=new AtomicInteger(110);

    private CH934XManager() {
        connectionMap=new HashMap<>();
    }

    /**
     * 初始化上下文，注册动态广播监听设备状态变化
     * @param application
     */
    public void init(Application application){
        usbManager= (UsbManager) application.getSystemService(Context.USB_SERVICE);
        registerBroadcast(application);
    }

    /**
     * 枚举当前的 CH934X设备
     * @return 设备列表
     * @throws UartLibException
     */
    public ArrayList<UsbDevice> enumDevice() throws UartLibException {
        if(usbManager==null){
            throw new UartLibException("invoke method init() firstly in Application");
        }
        //获取到所有目标USB设备
        return UsbUtil.getCurrentDevices(usbManager);
    }

    /**
     * 根据UsbDevice获取芯片类型
     * @param usbDevice USB设备
     * @return 如果为null则表示参数USB设备并不是CH934X
     */
    public ChipType getChipType(@NonNull UsbDevice usbDevice){
        return ChipValidUtil.getChipType(usbDevice);
    }

    /**
     * 打开设备
     * @param usbDevice
     * @return true 成功；false 失败
     * @throws UartLibException
     * @throws NoPermissionException
     * @throws ChipException
     */
    public boolean openDevice(@NonNull UsbDevice usbDevice)throws UartLibException, NoPermissionException, ChipException {
        if(usbManager==null){
            throw new UartLibException("invoke method init() firstly in Application");
        }

        if(!UsbUtil.hasPermission(usbManager,usbDevice)){
            throw new NoPermissionException("No permission: "+usbDevice.toString());
        }

        //获取芯片信息
        ChipType chipType = ChipValidUtil.getChipType(usbDevice);
        if(chipType==null){
            throw new ChipException("chip is unknown ");
        }
        ChipEntity chipInformation = ChipInformationUtil.getChipInformation(usbManager, usbDevice, chipType);
        if(chipInformation==null){
            throw new ChipException("the chip contains incorrect interface");
        }

        //打开设备
        if(isConnected(usbDevice)){
            //已经打开
            return true;
        }
        Connection connection=new Connection(usbManager,usbDevice,chipInformation);
        connection.init();
        connectionMap.put(usbDevice,connection);
        return true;
    }

    /**
     * 请求设备的打开权限
     * @param context
     * @param usbDevice
     * @throws UartLibException
     */
    public void requestPermission(@NonNull Context context,@NonNull UsbDevice usbDevice) throws UartLibException {
        if(usbManager==null){
            throw new UartLibException("invoke method init() firstly in Application");
        }
        //获取到所有目标USB设备
        ArrayList<UsbDevice> currentDevices = UsbUtil.getCurrentDevices(usbManager);
        //检查权限
        if(!UsbUtil.hasPermission(usbManager,usbDevice)){
            UsbUtil.requestPermission(usbManager,context,usbDevice,atomicInteger.getAndIncrement());
        }
    }

    private void registerBroadcast(@NonNull Context context){
        IntentFilter filter=new IntentFilter();
        filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        filter.addAction(Constant.REQUEST_PERMISSION_STRING);
        receiver=new UsbReceiver();
        context.registerReceiver(receiver,filter);
        receiver.setStateListener(this);
    }


    /**
     * 监听设备的状态变化
     * @param usbStateListener
     */
    public void setUsbStateListener(@NonNull IUsbStateChange usbStateListener) {
        this.iUsbStateChange=usbStateListener;
    }

    private void unRegisterBroadcast(@NonNull Context context){
        context.unregisterReceiver(receiver);
    }


    /**
     * 获取设备的串口数目
     * @param usbDevice
     * @return 返回串口数目;如果为负,说明获取失败
     */
    public int getSerialCount(@NonNull UsbDevice usbDevice){
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            return 0;
        }
        return connection.getSerialCount();
    }

    /**
     *  设置串口参数
     * @param usbDevice USB设备
     * @param serialNumber 串口号
     * @param baud 波特率
     * @param dataBit 数据位
     * @param stopBit 停止位
     * @param parityBit 校验位
     * @param flow 流控
     * @return true 设置成功;false 设置失败
     * @throws UartLibException
     * @throws ChipException
     */
    public boolean setSerialParameter(@NonNull UsbDevice usbDevice,int serialNumber,int baud,
                                   int dataBit,int stopBit,int parityBit,boolean flow) throws UartLibException, ChipException {
        //获取Connection
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            throw new UartLibException("this usbDevice isn't connected");
        }
        return connection.setSerialParameter(serialNumber, baud, dataBit, stopBit, parityBit,flow);
    }

    /**
     * 发送数据
     * @param usbDevice USB设备
     * @param serialNumber 串口号
     * @param data 待发送的数据
     * @param length 待发送的数据的长度
     * @param timeout 超时时间
     * @return 发送成功的数据的长度
     * @throws UartLibException
     * @throws ChipException
     */
    public int writeData(@NonNull UsbDevice usbDevice, int serialNumber, byte[] data, int length, int timeout) throws UartLibException, ChipException {
        //获取Connection
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            throw new UartLibException("this usbDevice isn't connected");
        }
        return connection.write(serialNumber, data, length,1000,timeout);
    }

    /*public void registerDataCallback(@NonNull UsbDevice usbDevice, IDataCallback dataCallback){
        dataCallbackMap.put(usbDevice,dataCallback);
        Connection connection = connectionMap.get(usbDevice);
        if(connection!=null){
            connection.setDataCallback(dataCallback);
        }
    }*/

    /**
     * 读取数据
     * @param usbDevice USB设备
     * @param serialNumber 串口号
     * @return 读取到的数据
     * @throws ChipException
     */
    public byte[] readData(@NonNull UsbDevice usbDevice,int serialNumber)throws ChipException{
        Connection connection = connectionMap.get(usbDevice);
        if(connection!=null){
            return connection.readDataFromBuffer(serialNumber,9,200);
        }
        throw new ChipException("this device isn't connected");
    }

    /**
     * 获取当前串口的模式。初始默认状态为普通模式
     * @param usbDevice USB设备
     * @param serialNumber 串口序号
     * @return Mode.NORMAL 普通模式；Mode.HARDFLOW 硬件流控模式；Mode.GPIO GPIO模式；
     * @throws UartLibException
     */
    public Mode getCurrentMode(UsbDevice usbDevice,int serialNumber) throws UartLibException {
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            throw new UartLibException("this usbDevice isn't connected");
        }
        return ModeAssist.getInstance().queryMode(usbDevice, serialNumber);
    }

    /**
     * 使能串口的GPIO功能，如果当前模式为硬件流控模式，需要先退出。
     * @param usbDevice USB设备
     * @param serialNumber 串口序号(CH9144为0-3)
     * @param enabled 使能状态
     * @return true 操作成功;false 操作失败
     * @throws UartLibException
     */
    public boolean enableGPIO(@NonNull UsbDevice usbDevice,int serialNumber,boolean enabled) throws UartLibException {
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            throw new UartLibException("this usbDevice isn't connected");
        }
        return connection.enableGPIO(serialNumber, enabled);
    }

    /**
     * 设置GPIO口的方向，成功后会同步至缓存
     * @param usbDevice USB设备
     * @param gpioNumber GPIO口序号
     * @param dir 方向
     * @return true 操作成功;false 操作失败
     * @throws UartLibException
     */
    public boolean setGPIODir(@NonNull UsbDevice usbDevice,int gpioNumber, @NonNull GPIO_DIR dir) throws UartLibException {
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            throw new UartLibException("this usbDevice isn't connected");
        }
        return connection.setGPIODir(gpioNumber, dir);
    }

    /**
     * 从缓存中获取某个GPIO的方向。初始每个GPIO初始默认方向是GPIO_DIR.IN
     * @param usbDevice USB设备
     * @param gpioNumber GPIO口序号
     * @return GPIO_DIR.IN IN方向;GPIO_DIR.OUT OUT方向
     * @throws UartLibException
     */
    public GPIO_DIR queryGPIODirFromCache(@NonNull UsbDevice usbDevice, int gpioNumber) throws UartLibException {
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            throw new UartLibException("this usbDevice isn't connected");
        }
        return GPIOAssist.getInstance().queryGPIODir(usbDevice,gpioNumber/3,gpioNumber);
    }


    /**
     * 设置GPIO口的值，成功后会同步至缓存
     * @param usbDevice USB设备
     * @param gpioNumber GPIO口序号
     * @param value GPIO值
     * @return true 操作成功;false 操作失败
     * @throws UartLibException
     */
    public boolean setGPIOValue(@NonNull UsbDevice usbDevice,int gpioNumber, @NonNull GPIO_VALUE value) throws UartLibException {
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            throw new UartLibException("this usbDevice isn't connected");
        }
        return connection.setGPIOValue(gpioNumber, value);
    }

    /**
     * 从硬件获取GPIO值,成功后会刷新缓存
     * @param usbDevice USB设备
     * @return true 操作成功;false 操作失败
     * @throws UartLibException
     */
    public boolean getGPIOValue(@NonNull UsbDevice usbDevice) throws UartLibException {
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            throw new UartLibException("this usbDevice isn't connected");
        }
        return connection.getGPIOValue();
    }

    /**
     * 从缓存中获取某个GPIO值。使用此方法前需要先使用getGPIOValue()方法，刷新缓存。初始每个GPIO初始默认值是GPIO_VALUE.LOW
     * @param usbDevice USB设备
     * @param gpioNumber GPIO口序号
     * @return GPIO_VALUE.HIGH 高电平;GPIO_VALUE.LOW 低电平
     * @throws UartLibException
     */
    public GPIO_VALUE queryGPIOValueFromCache(@NonNull UsbDevice usbDevice,int gpioNumber) throws UartLibException {
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            throw new UartLibException("this usbDevice isn't connected");
        }
        return connection.queryGPIOValueFromCache(gpioNumber);
    }


    /**
     * 设备是否已经被打开
     * @param usbDevice USB设备
     * @return true 已经被打开;false 没有被打开
     */
    public boolean isConnected(@NonNull UsbDevice usbDevice){
        Connection connection = connectionMap.get(usbDevice);
        if(connection==null){
            return false;
        }
        return connection.isAlive();
    }

    /**
     * 获取当前已经被打开的设备列表
     * @return 已经被打开的设备列表
     */
    public ArrayList<UsbDevice> getConnectedDevices(){
        Set<UsbDevice> usbDevices = connectionMap.keySet();
        ArrayList<UsbDevice> usbDeviceArrayList = new ArrayList<>(usbDevices);
        LogUtil.d("当前连接设备数目："+usbDeviceArrayList.size());
        return usbDeviceArrayList;

    }




    /**
     * 主动断开连接
     * @param usbDevice USB设备
     */
    public void disconnect(@NonNull UsbDevice usbDevice){
        LogUtil.d("主动断开连接 "+usbDevice.getDeviceName());
        //清除接收数据回调
        //registerDataCallback(usbDevice,null);
        //将流控恢复至默认
        int serialCount = getSerialCount(usbDevice);
        for (int i = 0; i < serialCount; i++) {
            boolean b=false;
            try {
                b = ModeAssist.getInstance().queryFlow(usbDevice, i);

            } catch (UartLibException e) {
                LogUtil.d(e.getMessage());
            }
            if(b){
                //如果流控打开，则关闭
                Connection connection = connectionMap.get(usbDevice);
                if(connection!=null){
                    connection.setFlow(i,false);
                }
            }
        }
        //清除设备模式记录
        ModeAssist.getInstance().clear(usbDevice);
        //清除设备GPIO记录
        GPIOAssist.getInstance().clear(usbDevice);

        Connection connection = connectionMap.get(usbDevice);
        if(connection!=null){
            connection.close();
            connectionMap.remove(usbDevice);
        }
    }

    /**
     * 被动断开连接
     * 用于设备移除，断开
     * @param usbDevice USB设备
     */
    private void passiveDeviceRemove(@NonNull UsbDevice usbDevice){
        LogUtil.d("设备移除："+usbDevice.getDeviceName());
        //模式状态清除
        ModeAssist.getInstance().clear(usbDevice);
        //清除设备GPIO记录
        GPIOAssist.getInstance().clear(usbDevice);
        //清除连接信息
        Connection connection = connectionMap.get(usbDevice);
        if(connection!=null){
            connection.close();
            connectionMap.remove(usbDevice);
        }
    }


    /**
     * 释放资源。断开所有连接设备,注销广播
     * @param context 上下文
     */
    public void close(@NonNull Context context){
        ArrayList<UsbDevice> connectedDevices = getConnectedDevices();
        for (UsbDevice connectedDevice : connectedDevices) {
            disconnect(connectedDevice);
        }
        unRegisterBroadcast(context);
    }


    @Override
    public void usbDeviceDetach(UsbDevice device) {
        LogUtil.d(String.format(Locale.getDefault(),"设备%s移除",device.getDeviceName()));
        passiveDeviceRemove(device);
        if(iUsbStateChange!=null){
            iUsbStateChange.usbDeviceDetach(device);
        }
    }

    @Override
    public void usbDeviceAttach(UsbDevice device) {
        LogUtil.d(String.format(Locale.getDefault(),"设备%s插入",device.getDeviceName()));
        if(iUsbStateChange!=null){
            iUsbStateChange.usbDeviceAttach(device);
        }
    }

    @Override
    public void usbDevicePermission(UsbDevice device, boolean result) {
        LogUtil.d(String.format(Locale.getDefault(),"设备%s请求权限结果："+result,device.getDeviceName()));
        if(iUsbStateChange!=null){
            iUsbStateChange.usbDevicePermission(device,result);
        }
    }
}
